package symbolesPackage;

import java.util.HashMap;
import java.util.Vector;

import ast.Expr;
import ast.JmmClass;
import ast.Loc;
import errors.TypeError;

public class TypeDef 
{
	
	public class UneMethode
	{
		private String nom;
		private String typeRetour;
		private Expr corps;
		private Loc location ;
		
		private Vector<String> nomsArguments; // noms des arguments , attention l'ordre est important !
		private Vector<String> typesArguments; // types des arguments , attention l'ordre est important !
		
		public UneMethode(String n, String tr, Expr c, Vector<String> nomsArgs , Vector<String> typesArgs  , Loc location )
		{
			if( nomsArgs.size() != typesArgs.size()  ){ throw new TypeError("erreur : nombre de noms et de types d'arguments incoherents"); }
			this.nom 		= n;
			this.typeRetour = tr;
			this.corps		= c;
			this.nomsArguments 	= nomsArgs;
			this.typesArguments = typesArgs;
			this.location 	= location ; 
		}
		
		public Vector<String> getProfil()
		{
			return this.typesArguments ;
		}

		public Loc getLocation() {
			return location;
		}

		public String getTypeRetour() {
			return typeRetour;
		}
	}
	public class UnConstructeur
	{
		private String nom ;
		private Expr corps;
		private Vector<String> nomsArguments; // noms des arguments , attention l'ordre est important !
		private Vector<String> typesArguments; // types des arguments , attention l'ordre est important !
		private Loc location ;
		
		public UnConstructeur(String nom , Expr corp ,Vector<String> argsN ,Vector<String> argsT , Loc location)
		{
			if( argsN.size() != argsT.size()  ){ throw new TypeError("erreur : nombre de noms et de types d'arguments incoherents"); }
			this.nom 				= nom ;
			this.corps 				= corp;
			this.typesArguments 	= argsT;
			this.nomsArguments		= argsN;
			this.location			= location ;
		}
		
		public Vector<String> getProfil()
		{
			return this.typesArguments ;
		}

		public Loc getLocation() {
			return location;
		}
		
	}
	public class UnAttribut
	{
		private String 	nom ;
		private String 	type ;
		private Loc 	location ;

		public UnAttribut(String nom , String type , Loc location)
		{
			this.nom = nom ; this.type = type ; this.location = location ;
		}
		
		public Loc getLocation(){return this.location;}

		public String getType() {
			return type;
		}

		public String getNom() {
			return nom;
		}
	}
	
	private String nomClasse;
	private String nomSuperClasse;
	private Vector<UnAttribut> sesAttributs; 
	private Vector<UnConstructeur> sesConstructeurs;
	private HashMap< String, Vector<UneMethode> > sesMethodes;
	private Loc location ;
	private JmmClass jmmClass ;
	
	public TypeDef(String nomclasse , String nomsuperclasse , Loc loc ,JmmClass jmmClass)
	{
		this.location			= loc ;
		this.nomClasse 			= nomclasse ;
		this.nomSuperClasse 	= nomsuperclasse;
		this.sesAttributs 		= new Vector<UnAttribut>();
		this.sesMethodes 		= new HashMap< String , Vector<UneMethode> >();
		this.sesConstructeurs 	= new Vector<UnConstructeur>();
		this.jmmClass			= jmmClass ;
	}
	
	
	
	public String getNomClasse(){return this.nomClasse ;}
	public JmmClass getJmm(){return this.jmmClass ;}
	public String toString(){return nomClasse ; }
	public Loc getLocation(){return this.location ;}
	public String getNomSuperClasse() {	return nomSuperClasse;}	
	
	public UnAttribut getAttribut( String nomAttribut )
	{
		for( int i = 0 ; i < this.sesAttributs.size() ; i++)
		{
			UnAttribut att = this.sesAttributs.elementAt(i);
			if(att.nom.equals( nomAttribut ) )return att ;
		}
		return null;
	}
	
	
	
	public void ajoutAttribut(  String leType , String leNom , Loc location )
	{
		UnAttribut att = getAttribut(leNom) ;
		if( att != null )
		{
			 throw new TypeError("la classe '"+this.nomClasse+"' possede deja un attribut '"+leNom+"' ( ici:'"+att.location+"') ", location ) ; 
		}
		
		att = new UnAttribut(leNom,leType , location );
		this.sesAttributs.add( att );
	}
	
	
	public  Vector<UnConstructeur>  getSesConstructeurs() 
	{
		return  this.sesConstructeurs ;
	}
	public void ajoutConstructeur( UnConstructeur mc ) throws TypeError 
	{
		String nom = mc.nom ;
		if( ! this.nomClasse.equals(nom)  )
		{
			 throw new TypeError("le constructeur '"+nom+"' n'as pas le meme nom que sa classe ('"+this.nomClasse+"') ", mc.location ) ; 
		}
		else
		{
			this.sesConstructeurs.add(mc);
		}
	}
	
	
	
	public  Vector<UneMethode>  getSesMethodes(String nomMethode) 
	{
		if( this.sesMethodes.containsKey(nomMethode) )	return this.sesMethodes.get(nomMethode) ;
		else return new Vector<UneMethode>() ;
	}
	
	public void ajoutMethode(  UneMethode mm )
	{
		
		Vector<UneMethode> v = getSesMethodes(mm.nom);
		
		// si c'ets le premier ajout on doit construire le vecteur 
		if( v.size() != 0  )
		{
			v.add(mm);
		}
		else
		{
			v.add(mm);
			this.sesMethodes.put(mm.nom, v );
		}
	}



	public Vector<UnAttribut> getSesAttributs() {
		return sesAttributs;
	}
	


}
